package com.ms.msclient;
import java.io.File;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import com.monitor.rest.MsRequestUtil;
import com.system.comm.model.Page;
import com.system.comm.utils.FrameBeanUtil;
import com.system.comm.utils.FrameFileUtil;
import com.system.comm.utils.FrameJsonUtil;
import com.system.comm.utils.FrameNoUtil;
import com.system.comm.utils.FrameSpringBeanUtil;
import com.system.comm.utils.FrameStringUtil;
import com.system.comm.utils.FrameTimeUtil;

/**
 * 动态代理，需要注意的是，这里用到的是JDK自带的动态代理，代理对象只能是接口，不能是类
 * @author yuejing
 */
public class MsClientServiceProxy<T> implements InvocationHandler {

	private static final Logger LOGGER = LoggerFactory.getLogger(MsClientServiceProxy.class);
	private static final String VALUE_START = "${";
	private static final String VALUE_END = "}";
	private Class<T> interfaceType;

	public MsClientServiceProxy(Class<T> intefaceType) {
		this.interfaceType = intefaceType;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		if (Object.class.equals(method.getDeclaringClass())) {
			return method.invoke(this, args);
		}
		MsClient msClient = interfaceType.getAnnotation(MsClient.class);
		if (msClient == null) {
			return method.invoke(this, args);
		}
		String serviceId = msClient.value();
		if (FrameStringUtil.isEmpty(serviceId)) {
			serviceId = msClient.defalut();
		}
		if (FrameStringUtil.isEmpty(serviceId)) {
			return method.invoke(this, args);
		}
		if (serviceId.startsWith(VALUE_START) && serviceId.endsWith(VALUE_END)) {
			// 从变量获取
			Environment env = FrameSpringBeanUtil.getBean(Environment.class);
			String serviceIdValue = env.getProperty(serviceId.substring(VALUE_START.length(), serviceId.length() - VALUE_END.length()));
			if (FrameStringUtil.isEmpty(serviceIdValue)) {
				serviceId = msClient.defalut();
			} else {
				serviceId = serviceIdValue;
			}
		}
		RequestMapping mapping = method.getAnnotation(RequestMapping.class);
		if (mapping == null) {
			LOGGER.error("请配置方法[{}.{}]的@RequestMapping注解", interfaceType.getName(), method.getName());
			return method.invoke(this, args);
		}
		String[] urlMapping = mapping.value();
		if (urlMapping== null || urlMapping.length == 0) {
			LOGGER.error("请配置方法[{}.{}]的@RequestMapping注解的请求地址", interfaceType.getName(), method.getName());
			return method.invoke(this, args);
		}
		MsClientResponse<Class<?>> frame = null;
		boolean isRequestBody = false;
		String url = urlMapping[0];
		Map<String, Object> params = new HashMap<String, Object>();
		Parameter[] parameters = method.getParameters();
		for (int i = 0; i < parameters.length; i ++) {
			Parameter parameter = parameters[i];
			RequestBody reqBody = parameter.getAnnotation(RequestBody.class);
			if (reqBody != null) {
				isRequestBody = true;
			}
			RequestParam reqParam = parameter.getAnnotation(RequestParam.class);
			if (reqParam == null) {
				// 处理对象
				Object value = args[i];
				if (value instanceof MultipartFile) {
					params.put("file", value);
				} else {
					Map<String, Object> paramMap = FrameBeanUtil.toMap(value);
					Iterator<Entry<String, Object>> entryKeyIterator = paramMap.entrySet().iterator();
					while (entryKeyIterator.hasNext()) {
						Entry<String, Object> e = entryKeyIterator.next();
						Object itemValue = e.getValue();
						if (itemValue instanceof Date) {
							params.put(e.getKey(), FrameTimeUtil.parseString((Date) itemValue));
						} else {
							params.put(e.getKey(), itemValue);
						}
					}
				}
			} else {
				String key = reqParam.value();
				Object value = args[i];
				if (value instanceof Date) {
					params.put(key, FrameTimeUtil.parseString((Date) value));
				} else {
					params.put(key, value);
				}
			}
		}
		ParameterizedType pt = (ParameterizedType) method.getGenericReturnType();
		Class<?> methodType = method.getReturnType();
		if (ResponseEntity.class.getName().equals(methodType.getTypeName())) {
			// 下载文件
			return download(serviceId, url, params);
		}
		Class<?> clazz = null;
		Class<?> pageClazz = null;
		Object object = pt.getActualTypeArguments()[0];
		if (object instanceof ParameterizedType) {
			ParameterizedType cldPt = (ParameterizedType) object;
			clazz = (Class<?>) cldPt.getRawType();
			if (Page.class.getName().equals(clazz.getName())) {
				pageClazz = (Class<?>) cldPt.getActualTypeArguments()[0];
			}
		} else {
			clazz = (Class<?>) pt.getActualTypeArguments()[0];
		}

		String[] consumes = mapping.consumes();
		if (consumes != null && consumes.length > 0) {
			for (String consume : consumes) {
				if (MediaType.MULTIPART_FORM_DATA_VALUE.equals(consume)) {
					// 上传文件
					frame = upload(mapping, serviceId, url, params, args, clazz);
					return frame;
				}
			}
		}
		if (isRequestBody) {
			if (MsClientServiceBeanRegistry.isShowLog()) {
				LOGGER.info("调用前，参数：{}", FrameJsonUtil.toString(params));
			}
			frame = MsRequestUtil.postMsClientJsonBody(serviceId, url, params, clazz, pageClazz);
			if (MsClientServiceBeanRegistry.isShowLog()) {
				LOGGER.info("调用后，结果：{}", FrameJsonUtil.toString(frame));
			}
		} else {
			if (MsClientServiceBeanRegistry.isShowLog()) {
				LOGGER.info("调用前，参数：{}", FrameJsonUtil.toString(params));
			}
			frame = MsRequestUtil.postMsClient(serviceId, url, params, clazz, pageClazz);
			if (MsClientServiceBeanRegistry.isShowLog()) {
				LOGGER.info("调用后，结果：{}", FrameJsonUtil.toString(frame));
			}
		}
		return frame;
	}

	private ResponseEntity<byte[]> download(String serviceId, String url, Map<String, Object> params) {
		return MsRequestUtil.downloadMsClient(serviceId, url, params);
	}

	private MsClientResponse<Class<?>> upload(RequestMapping mapping, String serviceId, String url, Map<String, Object> params, Object[] args, Class<?> clazz) {
		MsClientResponse<Class<?>> frame = new MsClientResponse<>();
		MultipartFile multipartFile = null;
		Iterator<Entry<String, Object>> entryKeyIterator = params.entrySet().iterator();
		String fileKey = null;
		while (entryKeyIterator.hasNext()) {
			Entry<String, Object> e = entryKeyIterator.next();
			Object itemValue = e.getValue();
			if (itemValue instanceof MultipartFile) {
				multipartFile = (MultipartFile) itemValue;
				fileKey = e.getKey();
			}
		}
		if (multipartFile == null) {
			return new MsClientResponse<>(-200, "上传文件不能为空!");
		}
		Environment env = FrameSpringBeanUtil.getBean(Environment.class);
		String path = env.getProperty("spring.http.multipart.location");
		if (FrameStringUtil.isEmpty(path)) {
			path = System.getProperty("user.dir") + File.separator + "upload_tmp";
		}
		//获得原始文件名
		String orgName = multipartFile.getOriginalFilename();
		//String fileId = null;
		//String fileExt = orgName.substring(orgName.lastIndexOf(".") + 1, orgName.length());
		path += File.separator + FrameTimeUtil.getTodayYyyyMmDd() + File.separator + FrameNoUtil.uuid();
		FrameFileUtil.createDir(path);
		String name = orgName;
		File file = new File(path + File.separator + name);
		try {
			multipartFile.transferTo(file);
		} catch (Exception e) {
			e.printStackTrace();
		}
		if (FrameStringUtil.isNotEmpty(fileKey)) {
			params.remove(fileKey);
		}
		if (MsClientServiceBeanRegistry.isShowLog()) {
			LOGGER.info("调用前，参数：{}", FrameJsonUtil.toString(params));
		}
		frame = MsRequestUtil.uploadMsClient(serviceId, url, file, params, clazz);
		if (MsClientServiceBeanRegistry.isShowLog()) {
			LOGGER.info("调用后，结果：{}", FrameJsonUtil.toString(frame));
		}
		return frame;
	}

}
